<?php
/**
 * Nweb Framework
 *
 * This script is protected by copyright. It's use, copying, modification
 * and distribution without written consent of the author is prohibited.
 *
 * @category    source
 * @package     pl.nweb.framework.cache
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt  GNU Lesser General Public
 * @version     3.0 $Id: cache.php 435 2011-09-23 13:24:30Z krzysztof.kardasz $
 * @link        http://code.google.com/p/nweb-framework/
 * @link        http://framework.nweb.pl
 */

namespace pl\nweb\framework;

/**
 * Cache
 *
 * @category    source
 * @package     pl.nweb.framework.cache
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @version     3.0 $Id: cache.php 435 2011-09-23 13:24:30Z krzysztof.kardasz $
 */
abstract class Cache implements cache\iCache
{
    /**
     * Flaga, cache włączony / wyłączony
     *
     * @var boolean
     */
    private $_enabled = true;

    /**
     * Flaga, automatyczne generowanie hashu z klucza
     *
     * @var boolean
     */
    private $_hash = true;

    /**
     * Flaga, automatyczne serializacja danych
     *
     * @var boolean
     */
    private $_serialize = true;

    /**
     * Czas ważności pliku
     *
     * @var integer
     */
    private $_lifetime = 7200;

    /**
     * Prefiks
     *
     * @var string|null
     */
    private $_prefix = null;

    /**
     * Obiekt dziennika zdarzeń
     *
     * @var null|\pl\nweb\framework\log\iLog
     */
    private $_logger = null;

    /**
     * Opcja, włączenie / wyłączenie cache
     */
    const OPTION_ENABLED = 'enabled';

    /**
     * Opcja, automatyczny hash klucza
     */
    const OPTION_HASH = 'hash';

    /**
     * Opcja, czas ważności pliku
     */
    const OPTION_LIFETIME = 'lifetime';

    /**
     * Opcja, serializacja danych
     */
    const OPTION_SERIALIZE = 'serialize';

    /**
     * Opcja, prefiks
     */
    const OPTION_PREFIX = 'prefix';

    /**
     * Opcja, obiekt dziennika zdarzeń
     */
    const OPTION_LOGGER = 'logger';

    /**
     * Tryb czyszczenia, wszystko
     */
    const CLEAN_MODE_ALL = 10;

    /**
     * Tryb czyszczenia, przedawnione
     */
    const CLEAN_MODE_OLD = 12;

    /**
     * Konstruktor
     *
     * @param array $options konfiguracja
     */
    public function __construct (array $options = array())
    {
        if(isset($options[self::OPTION_ENABLED])) {
            $this->setEnabled($options[self::OPTION_ENABLED]);
        }

        if(isset($options[self::OPTION_LIFETIME])) {
            $this->setLifetime($options[self::OPTION_LIFETIME]);
        }

        if(isset($options[self::OPTION_PREFIX])) {
            $this->setPrefix($options[self::OPTION_PREFIX]);
        }

        if(isset($options[self::OPTION_LOGGER])) {
            $this->setLogger($options[self::OPTION_LOGGER]);
        }

        if(isset($options[self::OPTION_HASH])) {
            $this->setHash($options[self::OPTION_HASH]);
        }

        if(isset($options[self::OPTION_SERIALIZE])) {
            $this->setSerialize($options[self::OPTION_SERIALIZE]);
        }
    }

    /**
     * Definiowanie flagi czy cache jest włączony
     *
     * @param boolean $flag flaga
     * @return \pl\nweb\framework\Cache
     */
    public function setEnabled ($flag)
    {
        $this->_enabled = (boolean)$flag;
        return $this;
    }

    /**
     * Zwraca flagę czy cache jest włączony
     *
     * @return boolean
     */
    public function isEnabled ()
    {
        return $this->_enabled;
    }

    /**
     * Definiowanie flagi czy hash klucza jest włączony
     *
     * @param boolean $flag flaga
     * @return \pl\nweb\framework\Cache
     */
    public function setHash ($flag)
    {
        $this->_hash = (boolean)$flag;
        return $this;
    }

    /**
     * Zwraca flagę czy cache jest włączony
     *
     * @return boolean
     */
    public function isHash ()
    {
        return $this->_hash;
    }

    /**
     * Formatowanie klucza
     *
     * @todo   Testy wydajnościowe, optymalizacja długich kluczy
     * @param  string $key nazwa klucza
     * @return string
     * @throws \pl\nweb\framework\cache\Exception
     */
    protected function _parseKey ($key)
    {
        if($this->isHash()) {
            $hash = sha1($key) . md5($key);
            return $this->getPrefix() . $hash;
        }

        if(!preg_match('/^[a-z0-9_]+$/i', $key)) {
            throw new cache\Exception ('Invalid key  "' . $key . '", must use only [a-zA-Z0-9_]');
        }
        return $this->getPrefix() . $key;
    }

    /**
     * Definiowanie czasu ważności
     *
     * @param integer $lifetime czas ważności pliku
     * @return \pl\nweb\framework\Cache
     */
    public function setLifetime ($lifetime)
    {
        $this->_lifetime = (int)$lifetime;
        return $this;
    }

    /**
     * Zwraca czas ważności
     *
     * @return integer
     */
    public function getLifetime ()
    {
        return $this->_lifetime;
    }

    /**
     * Definiowanie flagi czy serializacja jest włączona
     *
     * @param boolean $flag flaga
     * @return \pl\nweb\framework\Cache
     */
    public function setSerialize ($flag)
    {
        $this->_serialize = (boolean)$flag;
        return $this;
    }

    /**
     * Zwraca flagę czy serializacja jest włączona
     *
     * @return boolean
     */
    public function isSerialize ()
    {
        return $this->_serialize;
    }

    /**
     * Definiowanie prefiksu dla nazw kluczy
     *
     * @param string $prefix prefiks
     * @return \pl\nweb\framework\Cache
     */
    public function setPrefix ($prefix)
    {
        $prefix = (string)$prefix;
        if(!preg_match('/^[a-z0-9_]+$/i', $prefix)) {
            throw new cache\Exception ('Invalid prefix  "' . $prefix . '", must use only [a-zA-Z0-9_]');
        }

        $this->_prefix = $prefix;
        return $this;
    }

    /**
     * Zwraca prefiks dla nazw kluczy
     *
     * @return null|string
     */
    public function getPrefix ()
    {
        return $this->_prefix;
    }

    /**
     * Definiowanie obiektu dziennika zdarzeń
     *
     * @param \pl\nweb\framework\log\iLog $logger obiekt dziennika zdarzeń
     * @return \pl\nweb\framework\Cache
     */
    public function setLogger (\pl\nweb\framework\log\iLog $logger)
    {
        $this->_logger = $logger;
        return $this;
    }

    /**
     * Zwraca obiekt dziennika zdarzeń
     *
     * @return null|\pl\nweb\framework\log\iLog
     */
    public function getLogger ()
    {
        return $this->_logger;
    }

    /**
     * Zwraca flagę czy dane są obecne i wyświetla na ekran
     * Buforowanie danych
     *
     * @param  string $key nazwa klucza
     * @return boolean
     */
    public function start ($key)
    {
        if(($data = $this->get($key)) !== false) {
            echo $data;
            return true;
        }
        ob_start();
        return false;
    }

    /**
     * Zapisuje dane i wyświetla dane z buforu
     *
     * @param  string $key nazwa
     * @param  array $options konfiguracja
     * @return boolean
     */
    public function end ($key, array $options = array())
    {
        $data = ob_get_contents();
        $flag = $this->save($key, $data, $options);
        ob_end_flush();
        echo $data;
        return $flag;
    }
}